#include "../mpmc_queue.h"

#include <iostream>
#include <thread>


void singleProducerAndSingleConsumer()
{
  good_cptl::MPMCQueue<int, 1024> queue;
  std::thread producer([&queue]() {
    for (int i = 0; i < 1000000; ++i) {
      while (!queue.enqueue(i)) {
        continue;
      }
    }
  });

  std::thread consumer([&queue]() {
    int value;
    for (int i = 0; i < 1000000; ++i) {
      while (!queue.dequeue(value)) {
        continue;
      }
      std::cout << "Consumer: " << value << std::endl;
    }
  });
  producer.join();
  consumer.join();
}



void multiProducerAndSingleConsumer()
{
  good_cptl::MPMCQueue<int, 1024> queue;
  std::thread producers[4];
  for (int i = 0; i < 4; ++i) {
    producers[i] = std::thread([&queue, i]() {
      for (int j = 0; j < 1000000; ++j) {
        while (!queue.enqueue(i * 1000000 + j)) {
          continue;
        }
      }
    });
  }
  std::thread consumer([&queue]() {
    int value;
    for (int i = 0; i < 4000000; ++i) {
      while (!queue.dequeue(value)) {
        continue;
      }
      std::cout << "Consumer: " << value << std::endl;
    }
  });
  for (auto& t : producers) {
    t.join();
  }
  consumer.join();
}

void multiProducerAndMultiConsumer()
{
  good_cptl::MPMCQueue<int, 1024> queue;
  std::thread producers[4];
  std::thread consumers[4];
  for (int i = 0; i < 4; ++i) {
    producers[i] = std::thread([&queue, i]() {
      for (int j = 0; j < 1000000; ++j) {
        while (!queue.enqueue(i * 1000000 + j)) {
          continue;
        }
      }
    });
  }
  for (int i = 0; i < 4; ++i) {
    consumers[i] = std::thread([&queue]() {
      int value;
      for (int j = 0; j < 1000000; ++j) {
        while (!queue.dequeue(value)) {
          continue;
        }
        std::cout << "Consumer " << value << std::endl;
      }
    });
  }
  for (auto& t : producers) {
    t.join();
  }
  for (auto& t : consumers) {
    t.join();
  }
}



void testDynamicMPMCQueue()
{
  good_cptl::SysThreadSafeArena alloc;
  good_cptl::DynamicMPMCQueue<std::string> queue(1024, alloc);

  // for (int i = 0; i < 10000; i++) {
  //   queue.enqueue("Producer: " + std::to_string(i));
  // }
  // std::cout << "size: " << queue.size() << std::endl;

  // std::string value;
  // for (int i = 0; i < 10000; i++) {
  //   while (!queue.dequeue(value)) {
  //     std::this_thread::yield();
  //   }
  //   std::cout << "Dequeued: " << value << std::endl;
  //   std::cout << "size: " << queue.size() << std::endl;
  // }


  for (int i = 0; i < 1000000; i++) {
    while (!queue.enqueue("Producer: " + std::to_string(i))) {
      std::this_thread::yield();
    }
    if (i % 100000 == 0) {
      std::cout << "size: " << queue.size() << std::endl;
    }
  }

  for (int i = 0; i < 1000000; i++) {
    std::string value;
    while (!queue.dequeue(value)) {
      std::this_thread::yield();
    }
    std::cout << "Consumer: " << value << std::endl;
  }


  // std::thread producer([&] {
  //   for (int i = 0; i < 1000000; ++i) {
  //     while (!queue.enqueue("Producer: " + std::to_string(i))) {
  //       continue;
  //     }
  //   }
  // });
  // std::thread consumer([&] {
  //   std::string value;
  //   for (int i = 0; i < 1000000; ++i) {
  //     while (!queue.dequeue(value)) {
  //       std::this_thread::yield();
  //     }
  //     std::cout << value << std::endl;
  //   }
  // });

  // producer.join();
  // consumer.join();
}


int main()
{
  //singleProducerAndSingleConsumer();
  //multiProducerAndSingleConsumer();
  //multiProducerAndMultiConsumer();
  //std::cout << "All tests passed" << std::endl;
  testDynamicMPMCQueue();
  std::cout << "All tests passed" << std::endl;
  return 0;
}